use crate::features::sgraph::{self};
use crate::features::tasks::ddd::TaskSnapshot;
use crate::features::tasks::modelsdbo::{TaskDBO, TaskDiffProbaDBO};
use rbatis::crud::{Skip, CRUD};
use rbatis::executor::RbatisExecutor;
use rbatis::rbatis::Rbatis;
use chrono::prelude::*;
use rbatis::core::db::DBExecResult;

/// 通过 id 获取任务：EB Tasks
pub async fn find_task_by_id(rbatis_pool: &Rbatis, id: &str) -> Result<TaskDBO, rbatis::Error> {
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", id);
  let result = rbatis_pool.fetch_by_wrapper::<TaskDBO>(id_wrapper).await;
  result
}

#[py_sql(rbatis, "select * from task_diff_proba")]
pub async fn select_all_task_diff_proba(rbatis: &Rbatis) -> Vec<TaskDiffProbaDBO> {
  todo!()
}


pub async fn upsert_snapshot(
  rbatis_pool: &Rbatis,
  snapshot: &TaskSnapshot,
) -> Result<u64, rbatis::Error> {
  let mut succ_count = 0;
  let mut err = false;

  // 2. upsert edgedbo
  for edge in &snapshot.edges {
    let rs = sgraph::services::upsert_edgedbo(rbatis_pool, edge).await;
    if rs.is_ok() {
      succ_count += 1;
    } else {
      err = true;
      break;
    }
  }

  println!("3. upsert taskdbo err={}", err);
  // 3. upsert taskdbo
  if err == false {
    let task = &snapshot.task;
    let rs = self::upsert_task(rbatis_pool, task).await;
    if rs.is_ok() {
      succ_count += 1;
    } else {
      err = true;
    }
  }

  if err {
    Err(rbatis::Error::E(format!(
      "could not upsert snapshot={:?}",
      snapshot
    )))
  } else {
    Ok(succ_count)
  }
}

/// (user_abc) is (tester) of (quiz_xyz)
/// upsert edge by source,predicate,object
pub async fn upsert_task(rbatis_pool: &Rbatis, task: &TaskDBO) -> Result<TaskDBO, rbatis::Error> {
  let edge_dbo = self::find_task_by_id(rbatis_pool, &task.id).await;
  if edge_dbo.is_ok() {
    let updated = update_task(rbatis_pool, task).await;
    println!("upsert_task updated={:?}", updated);
  } else {
    let inserted = insert_task(rbatis_pool, task).await;
    println!("upsert_task inserted={:?}", inserted);
  }

  self::find_task_by_id(rbatis_pool, &task.id).await
}

/// insert edge by source,predicate,object
pub async fn insert_task(
  rbatis_pool: &Rbatis,
  task: &TaskDBO,
) -> Result<DBExecResult, rbatis::Error> {
  let now: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`

  let task_dbo = TaskDBO {
    created_at: now.timestamp(),
    updated_at: now.timestamp(),
    ..task.to_owned()
  };

  let db_result = rbatis_pool
    .save::<TaskDBO>(&task_dbo, &[Skip::Value(bson2::Bson::Null)])
    .await;

  db_result
}

/// update edge by source,predicate,object
pub async fn update_task(rbatis_pool: &Rbatis, task: &TaskDBO) -> Result<u64, rbatis::Error> {
  let now: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
  let id_wrapper = rbatis_pool.new_wrapper().eq("id", task.id.to_owned());

  let task_dbo = TaskDBO {
    updated_at: now.timestamp(),
    ..task.to_owned()
  };

  let updated = rbatis_pool
    .update_by_wrapper(
      &task_dbo,
      id_wrapper,
      &[Skip::Value(bson2::Bson::Null), Skip::Column("id")],
    )
    .await;

  updated
}
